package edu.ptu.shortlink.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import edu.ptu.shortlink.admin.common.biz.user.UserContext;
import edu.ptu.shortlink.admin.common.convention.exception.ClientException;
import edu.ptu.shortlink.admin.common.convention.result.Result;
import edu.ptu.shortlink.admin.dao.entity.GroupDO;
import edu.ptu.shortlink.admin.dao.mapper.GroupMapper;
import edu.ptu.shortlink.admin.dto.req.ShortLinkGroupSortReqDTO;
import edu.ptu.shortlink.admin.dto.req.ShortLinkGroupUpdateReqDTO;
import edu.ptu.shortlink.admin.dto.resp.ShortLinkGroupRespDTO;
import edu.ptu.shortlink.admin.remote.ShortLinkRemoteService;
import edu.ptu.shortlink.admin.remote.dto.resp.ShortLinkGroupCountQueryRespDTO;
import edu.ptu.shortlink.admin.service.IGroupService;
import edu.ptu.shortlink.admin.toolkit.RandomGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static edu.ptu.shortlink.admin.common.constant.RedisCacheConstant.LOCK_GROUP_CREATE_KEY;

/**
 * @BelongsProject: shortlink
 * @BelongsPackage: edu.ptu.shortlink.admin.service.impl
 * @Author: HYJ
 * @CreateTime: 2025-04-25  16:54
 * @Description: 短链接分组业务实现层
 * @Version: 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GroupServiceImpl extends ServiceImpl<GroupMapper, GroupDO> implements IGroupService {

    private final RedissonClient redissonClient;

    /**
     * 后续适用springcloud调用
     */
    private final ShortLinkRemoteService shortLinkRemoteService;

    //从配置文件中获取最大分组数量
    @Value("${short-link.group.max-num}")
    private Integer maxGroupNum;


    //注册用户创建默认分组时调用此方法
    @Override
    public void saveGroup(String username, String groupName) {

        // 分布式锁，防止并发创建分组
        RLock lock = redissonClient.getLock(String.format(LOCK_GROUP_CREATE_KEY, username));
        lock.lock();

        try {
            LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                    .eq(GroupDO::getUsername, username)
                    .eq(GroupDO::getName, groupName);

            List<GroupDO> groupDOList = baseMapper.selectList(queryWrapper);

            if (CollUtil.isNotEmpty(groupDOList) && groupDOList.size() == maxGroupNum) {
                throw new ClientException(String.format("分组数量已达到上限: %d", maxGroupNum));
            }

            //避免生成同一gid
            String gid;
            do {
                gid = RandomGenerator.generateRandom();
            } while (!availableGid(gid));

            GroupDO groupDO = GroupDO.builder()
                    .gid(gid)
                    //默认为0
                    .sortOrder(0)
                    .username(username)
                    .name(groupName)
                    .build();

            baseMapper.insert(groupDO);
        } finally {
            lock.unlock();
        }
    }

    // 创建新分组时调用此方法
    @Override
    public void saveGroup(String groupName) {
        saveGroup(UserContext.getUsername(), groupName);
    }


    /**
     * 判断生成gid是否可用
     *
     * @param gid gid
     * @return T:可用 F:不可用
     */
    private Boolean availableGid(String gid) {
        LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class).eq(GroupDO::getGid, gid)
                //请求上下文中获取用户名
                .eq(GroupDO::getUsername, UserContext.getUsername());


        GroupDO hasGroupFlag = baseMapper.selectOne(queryWrapper);
        return hasGroupFlag == null;
    }


    @Override
    public List<ShortLinkGroupRespDTO> listGroup() {

        LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                //请求上下文中获取用户名
                .eq(GroupDO::getUsername, UserContext.getUsername())
                .orderByDesc(GroupDO::getSortOrder, GroupDO::getUpdateTime);

        List<GroupDO> groupDOList = baseMapper.selectList(queryWrapper);

        // 查询短链接数量
        Result<List<ShortLinkGroupCountQueryRespDTO>> listGid = shortLinkRemoteService
                .countShortLink(groupDOList.stream().map(GroupDO::getGid).toList());


        List<ShortLinkGroupRespDTO> result = BeanUtil.copyToList(groupDOList, ShortLinkGroupRespDTO.class);

        //遍历分组集合，将短链接数量赋值给分组
        result.forEach(each -> {
                    Optional<ShortLinkGroupCountQueryRespDTO> first = listGid.getData().stream()
                            .filter(item -> Objects.equals(item.getGid(), each.getGid()))
                            .findFirst();
                    first.ifPresent(item -> each.setCountShortLink(item.getShortLinkCount()));
                }
        );
        return result;
    }


    @Override
    public void updateGroup(ShortLinkGroupUpdateReqDTO requestParam) {

        LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class).eq(GroupDO::getUsername, UserContext.getUsername()).eq(GroupDO::getGid, requestParam.getGid());


        GroupDO groupDO = GroupDO.builder().name(requestParam.getName()).build();

        baseMapper.update(groupDO, updateWrapper);
    }

    @Override
    public void removeGroup(String gid) {

        LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class).eq(GroupDO::getGid, gid).eq(GroupDO::getUsername, UserContext.getUsername());

        baseMapper.delete(updateWrapper);
    }

    @Override
    public void sortGroup(List<ShortLinkGroupSortReqDTO> requestParam) {

        requestParam.forEach(item -> lambdaUpdate().eq(GroupDO::getGid, item.getGid()).eq(GroupDO::getUsername, UserContext.getUsername())

                .set(GroupDO::getSortOrder, item.getSortOrder()).update());
    }
}
